use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use super::{bar::NewBar, enums::Mark};

/// 分型对象
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FX {
    /// 标的代码
    pub symbol: String,
    /// 时间
    pub dt: DateTime<Utc>,
    /// 分型标记
    pub mark: Mark,
    /// 最高价
    pub high: f64,
    /// 最低价
    pub low: f64,
    /// 分型价格
    pub fx: f64,
    /// 构成分型的无包含关系K线
    pub elements: Vec<NewBar>,
    /// 缓存，用户缓存
    pub cache: HashMap<String, serde_json::Value>,
}

impl FX {
    /// 创建新的分型
    pub fn new(
        symbol: String,
        dt: DateTime<Utc>,
        mark: Mark,
        high: f64,
        low: f64,
        fx: f64,
        elements: Vec<NewBar>,
    ) -> Self {
        Self {
            symbol,
            dt,
            mark,
            high,
            low,
            fx,
            elements,
            cache: HashMap::new(),
        }
    }

    /// 获取无包含关系K线
    pub fn new_bars(&self) -> &[NewBar] {
        &self.elements
    }

    /// 获取原始K线
    pub fn raw_bars(&self) -> Vec<&super::bar::RawBar> {
        let mut result = Vec::new();
        for element in &self.elements {
            result.extend(element.raw_bars());
        }
        result
    }

    /// 分型力度字符串
    pub fn power_str(&self) -> &'static str {
        if self.elements.len() != 3 {
            return "未知";
        }

        let k1 = &self.elements[0];
        let k2 = &self.elements[1];
        let k3 = &self.elements[2];

        match self.mark {
            Mark::D => {
                if k3.close > k1.high {
                    "强"
                } else if k3.close > k2.high {
                    "中"
                } else {
                    "弱"
                }
            }
            Mark::G => {
                if k3.close < k1.low {
                    "强"
                } else if k3.close < k2.low {
                    "中"
                } else {
                    "弱"
                }
            }
        }
    }

    /// 成交量力度
    pub fn power_volume(&self) -> f64 {
        self.elements.iter().map(|x| x.vol).sum()
    }

    /// 构成分型的三根无包含K线是否有重叠中枢
    pub fn has_zs(&self) -> bool {
        if self.elements.len() != 3 {
            return false;
        }

        let zd = self.elements.iter().map(|x| x.low).fold(f64::NEG_INFINITY, f64::max);
        let zg = self.elements.iter().map(|x| x.high).fold(f64::INFINITY, f64::min);
        zg >= zd
    }

    /// 获取缓存值
    pub fn get_cache<T>(&self, key: &str) -> Option<T>
    where
        T: for<'de> Deserialize<'de>,
    {
        self.cache.get(key).and_then(|v| serde_json::from_value(v.clone()).ok())
    }

    /// 设置缓存值
    pub fn set_cache<T>(&mut self, key: &str, value: T)
    where
        T: Serialize,
    {
        if let Ok(v) = serde_json::to_value(value) {
            self.cache.insert(key.to_string(), v);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::objects::enums::Mark;

    #[test]
    fn test_fx() {
        let dt = Utc::now();
        
        let new_bar1 = NewBar::new(
            "000001.SZ".to_string(),
            1,
            dt,
            crate::objects::enums::Freq::Day,
            10.0,
            11.0,
            12.0,
            9.0,
            1000.0,
            11000.0,
        );

        let new_bar2 = NewBar::new(
            "000001.SZ".to_string(),
            2,
            dt,
            crate::objects::enums::Freq::Day,
            11.0,
            10.0,
            12.0,
            9.0,
            1000.0,
            11000.0,
        );

        let new_bar3 = NewBar::new(
            "000001.SZ".to_string(),
            3,
            dt,
            crate::objects::enums::Freq::Day,
            10.0,
            9.0,
            12.0,
            8.0,
            1000.0,
            11000.0,
        );

        let fx = FX::new(
            "000001.SZ".to_string(),
            dt,
            Mark::D,
            12.0,
            8.0,
            9.0,
            vec![new_bar1, new_bar2, new_bar3],
        );

        assert_eq!(fx.new_bars().len(), 3);
        assert_eq!(fx.power_str(), "弱");
        assert_eq!(fx.power_volume(), 3000.0);
    }
} 